include(c.m4.common)

divert(-1)
define(ENDLAB, 5283) dnl

define(PROCASSIGN,` ')
define(ROUNDROBINASSIGN, ` ' )
define(RANGEASSIGN, ` ' )

define(RESET_STATS, `; ' )
define(PRINT_COUNTERS, `; ' )
define(START_COUNTERS, `; ' )
define(STOP_COUNTERS, `; ' )
define(cfree, `free' )

define(GETNUMCPU, `
{
	$1 = Sim_GetNumCpus();
}
')

define(LOCKDEC, `
	pthread_mutex_t $1;
')

define(LOCKINIT, `
{ 
	pthread_mutex_init(&$1,NULL);
}
')


define(LOCK,`
{ 
	pthread_mutex_lock(&($1));
}
')
	
define(UNLOCK,`
{ 
	pthread_mutex_unlock(&($1));
}
')


define(ALOCKDEC, `
	pthread_mutex_t $1[$2];
')

define(ALOCKINIT, `
{ 
     int count; 
     for (count = 0; count < $2; count++) { 
		pthread_mutex_init(&$1[count], NULL);
     } 
}
')

define(ALOCK, ` LOCK( $1[$2] ) ' )
define(AUNLOCK, ` UNLOCK( $1[$2] ) ' )
define(AULOCK, ` UNLOCK( $1[$2] ) ' )

define(SLINIT, `LOCKINIT($1)' )

define(SPINLOCK, ` LOCK( $1 ) ')

define(RELEASELOCK, ` UNLOCK($1) ')

define(BARRIER,`
{ 
	pthread_barrier_wait(&$1); 
}
')

define(SEMINIT, `
{
	$1 = SEMA_OPEN($2);
}')

define(ENQUEUE, `
{
	SEMA_WAIT($1);
}')

define(DEQUEUE, `
{
	SEMA_POST($1);
}')

define(SYS_MONINIT, ` 
{
  int mon_dum1, mon_dum2;

  ifelse(eval($2 > 0),1, `
  for (mon_dum1 = 0; mon_dum1 < $3; mon_dum1++)
    for (mon_dum2 = 0; mon_dum2 < $2; mon_dum2++) {
      $1[mon_dum1].count[mon_dum2] = 0;
      SEMINIT($1[mon_dum1].queue[mon_dum2], 0)
    }' ,)

  for (mon_dum1 = 0; mon_dum1 < $3; mon_dum1++) 
    SLINIT($1[mon_dum1].lock, 1)
}')
define(SYS_MENTER, `SPINLOCK($1[$2].lock);')
define(SYS_DELAY, `
{
  $1[$3].count[$2]++;
  RELEASELOCK($1[$3].lock)
  ENQUEUE($1[$3].queue[$2]);   
}')
define(SYS_CONTINUE, `
{
  if ($1[$3].count[$2]) {
    ($1[$3].count[$2])--;
    DEQUEUE($1[$3].queue[$2])   
  } else
    RELEASELOCK($1[$3].lock)   
  goto `L'ENDLAB;
`LGO'ENDLAB: ;
}')
define(SYS_MEXIT, `
{
  RELEASELOCK($1[$2].lock); `L'ENDLAB: ; 
  define(`ENDLAB', eval(ENDLAB+1))
}')

define(DECVAR, `
  struct $1TYP {                                                    
    pthread_mutex_t lock;
    ifelse(eval($2 > 0), 1, `volatile int count[$2]; volatile SEMA_T *queue[$2];',)
    $4                       
  } $1[$3];
')
    
define(CREATE, `
{
  if(pthread_create(&pthreads[pthreadCreateIndex++], NULL, (void*)$1, NULL) != 0)
  {
    perror("");
    fprintf (stderr, "Create failed on process %s\n","$1");
    exit (1);
  }
}')

define(CREATE_LITE,`
{
  if(pthread_create(&pthreads[pthreadCreateIndex++], NULL, (void*)$1, NULL) != 0)
  {
    perror("");
    fprintf (stderr, "Create failed on process %s\n","$1");
    exit (1);
  }
}')

define(SETSCHED,` ')


define(CLOCK, `
{
  if (gettimeofday (&sc_tp, (struct timezone *)0)) {
    perror (""); fprintf (stderr, "error in clock macro\n"); exit (1); }
  $1 = (sc_tp.tv_sec & 0x7ff)*1000000 + sc_tp.tv_usec;
} ')

define(MILICLOCK, `
{
  if (gettimeofday (&sc_tp, (struct timezone *)0)) {
    perror (""); fprintf (stderr, "error in clock macro\n"); exit (1); }
  $1 = (sc_tp.tv_sec)*1000 + sc_tp.tv_usec / 1000;
}')

define(SECONDCLOCK, `
{
  if (gettimeofday (&sc_tp, (struct timezone *)0)) { 
    perror (""); fprintf (stderr, "error in clock macro\n"); exit (1); } 
  $1 = ((double) sc_tp.tv_usec / 1000000.0) + (double) sc_tp.tv_sec;
}')


define(SUIF_HI_RES_TIMER_DATA_TYPE,`unsigned long')
define(SUIF_INIT_HI_RES_CLOCK,`')
define(SUIF_HI_RES_CLOCK_READ,`')
define(SUIF_DOUBLE_FROM_HI_RES_DATA,`')
define(SUIF_HI_RES_DIFF,`')


define(INCLUDES, `
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/file.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <float.h>
#include <simapi.h>
#include <pthread.h>

')

define(DEFNS, `
#define DEFAULT_PROC_START  4
#define DEFAULT_MAXPROC     MAX_NUM_CPUS
#define CACHE_LINE_SIZE     64 

extern int pthreadCreateIndex;
extern pthread_t pthreads[DEFAULT_MAXPROC];

#ifndef __SEMA_T__
#define __SEMA_T__
typedef struct _sema_t
{
    pthread_mutex_t lock;
    volatile int rsc_count;
} SEMA_T;
#endif
extern volatile SEMA_T* SEMA_OPEN(volatile int rsc_num);
extern void SEMA_WAIT(volatile SEMA_T *sema);
extern void SEMA_POST(volatile SEMA_T *sema);
');

define(MAIN_ENV, `
/* file "c.m4.less" */

INCLUDES
DEFNS
int spec_mode = 1;

  struct timeval sc_tp;
  unsigned Total_Proc=0x1;

  /* for hardware timer
  static unsigned cycleval;
  static unsigned *iotimer_addr = 0; */

  struct fop_str {
    LOCKDEC(lock)
    volatile unsigned   count;
  };
  typedef struct fop_str fop;

  unsigned finc(fop *a)
  {
    unsigned tmp;

    LOCK(a->lock)
    tmp = a->count;
    (a->count)++;
    UNLOCK(a->lock);
    return(tmp);
  }

  unsigned fdec(fop *a)
  {
    unsigned tmp;

    LOCK(a->lock)
    tmp = a->count;
    (a->count)--;
    UNLOCK(a->lock);
    return(tmp);
  }
int pthreadCreateIndex = 0;
pthread_t pthreads[DEFAULT_MAXPROC];

volatile SEMA_T* SEMA_OPEN(volatile int rsc_num)
{
        volatile SEMA_T* sema = (SEMA_T*)malloc(sizeof(SEMA_T));
	pthread_mutex_init(&sema->lock,NULL);
        sema->rsc_count = rsc_num;
        return sema;
};

void SEMA_WAIT(volatile SEMA_T *sema)
{
        while(1)
        {
                pthread_mutex_lock(&sema->lock);
                if(sema->rsc_count > 0)
                {
                        sema->rsc_count--;
                        pthread_mutex_unlock(&sema->lock);
                        return;
                } else
                {
                        pthread_mutex_unlock(&sema->lock);
                        while(sema->rsc_count == 0)
                                ;
                }
        }
};

void SEMA_POST(volatile SEMA_T *sema)
{
        pthread_mutex_lock(&sema->lock);
        sema->rsc_count++;
        pthread_mutex_unlock(&sema->lock);
}; 
')
define(EXTERN_ENV, `
  INCLUDES
  DEFNS
  extern int spec_mode;
  extern struct timeval sc_tp;
  extern unsigned Total_Proc;
')

define(INITENV, ` ')
define(MAIN_INITENV, `INITENV($1,$2)')
define(MAIN_END, ` ')

define(INITENV_LITE, ` ')
define(MAIN_INITENV_LITE, `INITENV_LITE($1,$2)')
define(MAIN_END_LITE,`{ exit (ifelse($1,,0,$1));}')

define(FOPDEC,`fop $1;')
define(FOPINIT,`
{
    LOCKINIT($1.lock)
    $1.count = ifelse($2,,0,$2);
}')
define(FINC,`finc(&($1))')
define(FDEC,`fdec(&($1))')

define(LITE_MALLOC,`malloc($1);')
define(G_MALLOC, `malloc ($1);')
define(G_FREE, `free ($1);')
define(NU_MALLOC, `malloc($1);')
define(SET_HOME, `{}')

define(WAIT_FOR_END,`
{ 
  int i;
  for (i = 0; i < $1; i++) {
      int result = pthread_join(pthreads[i], NULL);
      assert(result == 0);
    }
}
')

define(PIDTYPE,`int')
define(GETUNIQUEID,`Sim_GetMyId()')

define(INITID,`')
define(GETID,`
{
  $1 = Sim_GetMyId();
}')

define(THREADS_EQUAL,`($1 == $2)')

define(GET_PID,
`{
  $1 = Sim_GetMyId();
}')

define(AUG_OFF, ` ')
define(AUG_ON, ` ')
define(ST_LOG, ` ')
define(ST_INFO_INIT, ` ')
divert(0)
divert(-1)
define(MENTER,
`{
  AUG_OFF
  ST_LOG(ST_MENTER, (int *)&($1[$2].lock));
  SYS_MENTER($1, $2)
  AUG_ON
}')
    
define(MEXIT,
`{
  AUG_OFF
  ST_LOG(ST_MEXIT, (int *)&($1[$2].lock));
  SYS_MEXIT($1, $2)
  AUG_ON
}')
    
define(DELAY,
`{
  AUG_OFF
  ST_LOG(ST_DELAY, (int *)&($1[$3].lock));
  SYS_DELAY($1, $2, $3)
  AUG_ON
}')

define(CONTINUE,
`{
  AUG_OFF
  ST_LOG(ST_CONTINUE, (int *)&($1[$3].lock));
  SYS_CONTINUE($1, $2, $3)
  AUG_ON
}')

define(MONINIT, 
`{
  AUG_OFF
  SYS_MONINIT($1, $2, $3)
  AUG_ON
}')

define(BARDEC,`
pthread_barrier_t $1;
')
define(BARINIT,`
{ 
	bool flip = !inSimulation;
	if (flip) {
	    goto_sim();
	}
	pthread_barrier_init(&$1, NULL, Sim_GetNumCpus());
	if (flip) {
	    goto_real();
	}
}
')

define(GSDEC, `DECVAR($1,1,1,`int sub;')')

define(GSINIT, 
`{
  AUG_OFF
  SYS_MONINIT($1,1,1)
  $1[0].sub = 0;
  ST_INFO_INIT($1, ST_GS_LOCK, &($1[0].lock) )
  ST_INFO_INIT($1, ST_GS_QUEUE, &($1[0].queue[0]) )
  ST_LOG(ST_GS_INIT, (int *)&($1[0].lock))
  AUG_ON
}')

define(GETSUB, 
`{
  register int *lockAddr;
  AUG_OFF
  lockAddr = (int *)&($1[0].lock);
  ST_LOG(ST_GS_ENTER, lockAddr)
  SYS_MENTER($1, 0);
  if ($1[0].sub <= $3) {
    $2 = $1[0].sub++;
    ST_LOG(ST_GS_EXIT, lockAddr)
  } else {
    $2 = -1;
    if ($1[0].count[0] < ($4 - 1)) {
      SYS_DELAY($1,0,0)
    } else {
      $1[0].sub = 0;
    }
    ST_LOG(ST_GS_EXIT_LAST, lockAddr)
    SYS_CONTINUE($1,0,0)
  }
  SYS_MEXIT($1,0)
  AUG_ON
}')

define(ADEC, `DECVAR($1,1,1,`int pgdone, pbdone;')')

define(AINIT, 
`{
  AUG_OFF
  SYS_MONINIT($1,1,1)
  $1[0].pgdone = $1[0].pbdone = 0;
  AUG_ON
}')

/* aSKFOR(name, 0success/1exhaust/-1prog_end/nprob_end, P, getProb, <reset>) */
define(ASKFOR, 
`{
  register int *lockAddr;
  AUG_OFF
  lockAddr = (int *)&($1[0].lock);
  ST_LOG(ST_AS_ENTER, lockAddr)
  SYS_MENTER($1,0)
  if (($1[0].pgdone == 0) && ($1[0].pbdone != 0)) {
    if ($1[0].count[0] < ($3 - 1)) {
      SYS_DELAY($1,0,0)
    }
    ST_LOG(ST_AS_PROBDONE, lockAddr)
  } else {
    $2 = -2;
    while (($1[0].pgdone == 0) && ($1[0].pbdone == 0)) {
      $4
      if ($2 == 0) {
	ST_LOG(ST_AS_GOTTASK, lockAddr)
        SYS_CONTINUE($1,0,0)
      } else {
        if ($1[0].count[0] == ($3 - 1)) {
          $1[0].pbdone = 1;
        } else {
          SYS_DELAY($1,0,0)
	  ST_LOG(ST_AS_WORKWAIT, lockAddr)
        }
      }
    }
  }
  if ($1[0].pgdone != 0) {
    $2 = -1;
    ST_LOG(ST_AS_PROGDONE, lockAddr)
    SYS_CONTINUE($1,0,0)
  } else {
    $2 = $1[0].pbdone;
    if ($1[0].count[0] == 0) {
      $5
      $1[0].pbdone = 0;
    }
    ST_LOG(ST_AS_RESET, lockAddr)
    SYS_CONTINUE($1,0,0)
  }
  SYS_MEXIT($1,0)
  AUG_ON
}')

define(PROBEND, 
`{
  AUG_OFF
  SYS_MENTER($1,0)
  $1[0].pbdone = $2;
  SYS_MEXIT($1,0)
  AUG_ON
}')

define(PROGEND, 
`{
  AUG_OFF
  SYS_MENTER($1,0)
  $1[0].pgdone = 1;
  MAIN_END
  SYS_CONTINUE($1,0,0)
  SYS_MEXIT($1,0)
  AUG_ON
}')

define(NLOCKDEC, `LOCKDEC($1,$2,$3)')
define(NLOCKINIT, `LOCKINIT($1)')
define(NLOCK, `LOCK($1)')
define(NUNLOCK, `UNLOCK($1)')

define(PAUSEDEC, `DECVAR($1,2,ifelse($2,,1,$2),int flag;)')

define(PAUSEINIT,
`{
  int p_dummy;
  AUG_OFF
  for (p_dummy = 0; (p_dummy < ifelse($2,,1,$2)); p_dummy++)
    $1[p_dummy].flag = 0;
  SYS_MONINIT($1,2,ifelse($2,,1,$2))
  AUG_ON
}')

define(CLEARPAUSE,
`{
  register int *lockAddr; 
    
  AUG_OFF
  lockAddr = (int *)&($1[ifelse($2,,0,$2)].lock);
  ST_LOG(ST_EVENT_ENTER, lockAddr )
  SYS_MENTER($1,ifelse($2,,0,$2))
  $1[ifelse($2,,0,$2)].flag = 0;
  SYS_CONTINUE($1,0,ifelse($2,,0,$2))
  SYS_MEXIT($1,ifelse($2,,0,$2))
  ST_LOG(ST_EVENT_EXIT, lockAddr)
  AUG_ON
}')

define(SETPAUSE,
`{
  register int *lockAddr;

  AUG_OFF
  lockAddr = (int *)&($1[ifelse($2,,0,$2)].lock);
  ST_LOG(ST_EVENT_ENTER, lockAddr )
  SYS_MENTER($1,ifelse($2,,0,$2))
  $1[ifelse($2,,0,$2)].flag = 1;
  SYS_CONTINUE($1,0,ifelse($2,,0,$2))
  SYS_MEXIT($1,ifelse($2,,0,$2))
  ST_LOG(ST_EVENT_EXIT, lockAddr)
  AUG_ON
}')

define(EVENT,
`{
  register int *lockAddr;
  AUG_OFF
  lockAddr = (int *)&($1[ifelse($2,,0,$2)].lock);
  ST_LOG(ST_EVENT_ENTER, lockAddr )
  SYS_MENTER($1,ifelse($2,,0,$2))
  if ($1[ifelse($2,,0,$2)].flag) {
    SYS_DELAY($1,0,ifelse($2,,0,$2))
  }
  $1[ifelse($2,,0,$2)].flag = 1;
  SYS_CONTINUE($1,1,ifelse($2,,0,$2))
  SYS_MEXIT($1,ifelse($2,,0,$2))
  ST_LOG(ST_EVENT_EXIT, lockAddr)
  AUG_ON
}')


define(WAITPAUSE,
`{
  register int *lockAddr;

  AUG_OFF
  lockAddr = (int *)&($1[ifelse($2,,0,$2)].lock);
  ST_LOG(ST_PAUSE_ENTER,lockAddr)
  SYS_MENTER($1,ifelse($2,,0,$2))
  if (!$1[ifelse($2,,0,$2)].flag) {
    SYS_DELAY($1,0,ifelse($2,,0,$2))
  }
  SYS_CONTINUE($1,0,ifelse($2,,0,$2))
  SYS_MEXIT($1,ifelse($2,,0,$2))
  ST_LOG(ST_PAUSE_EXIT,lockAddr)
  AUG_ON
}')

define(PAUSE,
`{
  register int *lockAddr;
  AUG_OFF
  lockAddr = (int *)&($1[ifelse($2,,0,$2)].lock);
  ST_LOG(ST_PAUSE_ENTER, lockAddr )
  SYS_MENTER($1,ifelse($2,,0,$2))
  if (!$1[ifelse($2,,0,$2)].flag) {
    SYS_DELAY($1,1,ifelse($2,,0,$2))
  }
  $1[ifelse($2,,0,$2)].flag = 0;
  SYS_CONTINUE($1,0,ifelse($2,,0,$2))
  SYS_MEXIT($1,ifelse($2,,0,$2))
  ST_LOG(ST_PAUSE_EXIT, lockAddr )
  AUG_ON
}')


define(SRDEC, `DECVAR($1,2,ifelse($2,,1,$2),int flag;)')

define(SRINIT,
`{
  int s_dummy;
  AUG_OFF
  for (s_dummy = 0; (s_dummy < ifelse($2,,1,$2)); s_dummy++)
    $1[s_dummy].flag = 0;
  SYS_MONINIT($1,2,ifelse($2,,1,$2))
  AUG_ON
}')

define(SEND,
`{
  register int *lockAddr;
  AUG_OFF
  lockAddr = (int *)&($1[ifelse($3,,0,$3)].lock);
  ST_LOG(ST_SEND_ENTER, lockAddr )
  SYS_MENTER($1,ifelse($3,,0,$3))
  if ($1[ifelse($3,,0,$3)].flag) {
    SYS_DELAY($1,0,ifelse($3,,0,$3))
  }
  $2
  $1[ifelse($3,,0,$3)].flag = 1;
  SYS_CONTINUE($1,1,ifelse($3,,0,$3))
  SYS_MEXIT($1,ifelse($3,,0,$3))
  ST_LOG(ST_SEND_EXIT, lockAddr)
  AUG_ON
}')

define(RECEIVE,
`{
  register int *lockAddr;
  AUG_OFF
  lockAddr = (int *)&($1[ifelse($3,,0,$3)].lock);
  ST_LOG(ST_RECEIVE_ENTER, lockAddr )
  SYS_MENTER($1,ifelse($3,,0,$3))
  if (!$1[ifelse($3,,0,$3)].flag) {
    SYS_DELAY($1,1,ifelse($3,,0,$3))
  }
  $2
  $1[ifelse($3,,0,$3)].flag = 0;
  SYS_CONTINUE($1,0,ifelse($3,,0,$3))
  SYS_MEXIT($1,ifelse($3,,0,$3))
  ST_LOG(ST_RECEIVE_EXIT, lockAddr)
  AUG_ON
}')

define(HSEND,
`{
  register int *lockAddr;
  AUG_OFF
  lockAddr = (int *)&($1[ifelse($3,,0,$3)].lock);
  ST_LOG(ST_HSEND_ENTER, lockAddr)
  SYS_MENTER($1,ifelse($4,,0,$4))
  if ($1[ifelse($4,,0,$4)].flag) {
    SYS_DELAY($1,0,ifelse($4,,0,$4))
  }
  $3
  $1[ifelse($4,,0,$4)].flag = $2;
  SYS_CONTINUE($1,1,ifelse($4,,0,$4))
  SYS_MEXIT($1,ifelse($4,,0,$4))
  ST_LOG(ST_HSEND_EXIT, lockAddr)
  AUG_ON
}')

define(HRECEIVE,
`{
  register int *lockAddr;
  AUG_OFF
  lockAddr = (int *)&($1[ifelse($3,,0,$3)].lock);
  ST_LOG(ST_HRECEIVE_ENTER, lockAddr )
  SYS_MENTER($1,ifelse($4,,0,$4))
  while ((!($1[ifelse($4,,0,$4)].flag)) ||
               ($1[ifelse($4,,0,$4)].flag == $2)) {
    SYS_DELAY($1,1,ifelse($4,,0,$4))
  }
  $3
  $1[ifelse($4,,0,$4)].flag = 0;
  SYS_CONTINUE($1,0,ifelse($4,,0,$4))
  SYS_MEXIT($1,ifelse($4,,0,$4))
  ST_LOG(ST_HRECEIVE_EXIT, lockAddr ) 
  AUG_ON
}')

define(DYNMASK, `0xffff')

define(TASKDEC, `DECTVAR($1)')

define(TASKINIT,
`{
  AUG_OFF
  if (spy_gm->taskTblIndex < MAXTASKS) {
    (spy_gm->taskTbl[spy_gm->taskTblIndex]).name = "$1";
    $1[0].addr = (int *) (spy_gm->taskTblIndex << 16);
    ++(spy_gm->taskTblIndex);
  } else {
    fprintf(stderr,"Too many tasks\n"); exit(2);
  }
  ST_LOG(ST_TASK_INIT, (char *) $1[0].addr)
  AUG_ON
}')

define(BEGINTASK,
`{
  AUG_OFF
  ST_LOG(ST_BEGINTASK, ((int) $1[0].addr | (($2) & DYNMASK)))
  AUG_ON
}')

define(ENDTASK,
`{
  AUG_OFF
  ST_LOG(ST_ENDTASK, ((int) $1[0].addr | (($2) & DYNMASK)))
  AUG_ON
}')


define(WRITE_MEMORY_BARRIER, ` ')
define(MEMORY_BARRIER, ` ')

divert(0)
